/* kernel/page.c 
 * 
 * Copyright (C) 2021, 2022 intirain. 
 *
 * This file is part of evx. 
 * 
 * evx is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * evx is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with evx. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <kernel/init.h>
#include <kernel/module.h>
#include <kernel/mm.h>
#include <kernel/kmlog.h>
#include <kernel/kernel.h>

#include <arch/archinfo.h>

#include <lib/klist.h>
#include <sys/types.h>



/* I think, well, it looks like a buddy, but it's not...
 *
 * each '->data' points to the start of pages */
struct klist_node buddy_pages = KLIST_NODE_INIT(&buddy_pages);

int __init pages_init(void) {
	struct zone *z = kmem_zones;
	struct page *p = NULL;
	struct klist_node *n;

	int i;

	p = first_usable_ram;
	z = kmem_zones + page_id(p);

	n = kmalloc(sizeof(struct klist_node));

	if (unlikely(!n)) 
		panic("pages_init: out of memory");

	klist_init(n);

	n->data = p;
	klist_add(&buddy_pages, n);

	for (i = 0; i < usable_ram_pages; i++) {
		/* now it's linear, update, 2021-2-12 */

		if (!p) {
			do {
				z++;
			} while (z->type != ZONETYPE_RAM && z < (struct zone *) kmem_pages);

			if (unlikely(z >= (struct zone *) kmem_pages)) {
				kmlog(KERN_WARN "pages_init: unexpected end of zones\n");

				break;
			}

			p = z->pages;

			/* initial in buddy_pages */

			n = kmalloc(sizeof(struct klist_node));

			if (unlikely(!n)) 
				panic("pages_init: out of memory");

			n->data = p;
			klist_add(&buddy_pages, n);
		}

		++n->value;
		p++;
	}

	kmlog(KERN_INFO "%d pages total, %d pages usable\n", nr_ram_pages, usable_ram_pages);

	return 0;
}

/* NOTE that nr means number of pages but not the order. 
 * 
 * support buddy later ... */

struct page *getfreepages(int nr, int gfp_mask) {
	struct klist_node *n;
	struct page *p;

	int i;

	for (n = buddy_pages.next; n != &buddy_pages; n = n->next) 
		if (n->value >= nr) 
			/* got it */
			break;

	if (n == &buddy_pages) {
		kmlog(KERN_ERR "getfreepage: no enough pages: %d\n", nr);

		return NULL;
	}

	for (p = n->data, i = 0; i < nr; i++, p = p->next) 
		p->info |= 0xffu;

	n->data = p;
	n->value -= nr;

	return p;
}

